from . import reactor
from . import channel
import struct


class Protocol(object):
    def bind(self, channel):
        channel.owner = self
        self.channel = channel
        reactor.register_channel(channel)
        self.connected()
    @classmethod
    def connect(cls, host, port):
        chan = channel.SocketChannel.connect(host, port)
        proto = cls()
        proto.bind(chan)
        return proto
    def fileno(self):
        return self.channel.fileno()
    def write(self, data):
        self.channel.write(data)
    
    def connected(self):
        pass
    def disconnected(self):
        pass
    def received(self, data):
        pass

class StatefulProtocol(Protocol):
    class Count(object):
        def __init__(self, count):
            self.count = count
        def match(self, buffer):
            if self.count > len(buffer):
                return None
            if self.count < 0:
               self.count = len(buffer) 
            return buffer[:self.count], buffer[self.count:]
    
    class Pattern(object):
        def __init__(self, patterns, include_pattern = True):
            if isinstance(patterns, str):
                patterns = (patterns,)
            self.patterns = patterns
            self.include_pattern = include_pattern
        def match(self, buffer):
            for pat in self.patterns:
                i = buffer.find(pat)
                if i < 0:
                    continue
                j = i if self.include_pattern else i - len(pat)
                return buffer[:j], buffer[i:] 
            return None
    
    class Newline(Pattern):
        def __init__(self, include_pattern = True):
            Pattern.__init__(self, ["\n", "\r\n"], include_pattern = include_pattern)
    
    def connected(self):
        self._buffer = ""
        self._state = None
        self._matcher = None
    def received(self, data):
        self._buffer += data
        if self._state is None:
            self._state = self.protocol()
            self._matcher = self._state.next()
        try:
            while True:
                match = self._matcher.match(self._buffer)
                if match is None:
                    break
                data, self._buffer = match
                self._matcher = self._state.send(data)
        except StopIteration:
            self._state = None
            self._matcher = None
    def disconnected(self):
        if self._state is not None:
            try:
                self._state.throw(EOFError)
            except StopIteration:
                pass
    def protocol(self):
        raise NotImplementedError()

class FramingProtocol(StatefulProtocol):
    header = struct.Struct("!L")
    def protocol(self):
        data = yield self.Count(self.header.size)
        length, = self.header.unpack(data)
        data = yield self.Count(length)
        self.on_frame(data)
    
    def on_frame(self):
        pass
    
    def write(self, data):
        StatefulProtocol.write(self, self.header.pack(len(data)) + data)











 